package cn.edu.hit.kg.entity;

import cn.edu.hit.ObjectCacheUtils;
import cn.edu.hit.kg.data.GraphData;
import cn.edu.hit.kg.data.Node;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

public class BaseNode {
    public Node node;
    //    protected Node node;
    private static GraphData graphData = GraphData.getInstance();

    static Logger logger = LoggerFactory.getLogger(BaseNode.class);

    public BaseNode(Node node) {
        this.node = node;
    }

    public Node getNode() {
        return node;
    }

    public String getString(String key) {
        if (node.asMap().containsKey(key))
            return get(key, stringTransfer);
        return null;
    }

    public static final Function<Object, String> stringTransfer = v -> {
        String rawStr = v.toString();
        rawStr = unescapeText(rawStr);
        return StringUtils.strip(rawStr, "\"");

    };


    public <T> T get(String key, Function<Object, T> transfer) {
        return transfer.apply(node.get(key));
    }

    public static final Function<Object, List> listTransfer = v -> {
        ObjectMapper mapper = new ObjectMapper();
        List vlist = null;
        if (v instanceof List) {
            vlist = (List) v;
        } else {
            String vString = StringUtils.strip(v.toString(), "\"");
            vlist = JSONArray.fromObject(vString);
        }
        try {
            List rlist = new ArrayList<>();
            for (Object o : vlist) {
                rlist.add(stringTransfer.apply(o));
            }
            return rlist;
        } catch (Exception e) {
            logger.info("vlist:{}", vlist);

            e.printStackTrace();
            return Collections.emptyList();
        }
    };

    public static final Function<Object, ? extends Number> numberTransfer = v -> {
        ObjectMapper mapper = new ObjectMapper();
        try {
            return mapper.readValue(stringTransfer.apply(v), Number.class);
        } catch (Exception e) {

            return 0;
        }
    };

    public long getId() {
        return node.id();
    }

    public String getCode(){
        String outId = this.getString("outId");
        if (outId==null){
            return ""+this.getId();
        }
        return outId;
    }

    public static String escapeText(String text) {
        return text.replaceAll("\"", "\\\\\"");
    }

    public static String unescapeText(String text) {
        return text.replaceAll("\\\\\"", "\"");
    }

//    public static void main(String[] args) {
//        System.out.println(escapeText("[{\"channelId\":\"3058\",\"channelName\":\"5#瓦振\",\"valueType\":\"wave\",\"minValue\":0,\"maxValue\":\"10\",\"sampleValue\":\"0\",\"dataSource\":\"WebService\",\"type\":\"wave\"},{\"channelId\":\"3059\",\"channelName\":\"6#瓦振\",\"valueType\":\"wave\",\"minValue\":0,\"maxValue\":\"10\",\"sampleValue\":\"0\""));
//    }

    public JSONObject toJson() {
        JSONObject json = new JSONObject();
        json.put("id", this.getId());
        json.put("_type", StreamSupport.stream(this.node.labels().spliterator(), false).findFirst().orElse("!ErrorType"));
        this.node.asMap().keySet().forEach(key -> json.put(key, stringTransfer.apply(node.get(key))));
        return json;
    }

    @Override
    public String toString() {
        return toJson().toString();
    }

    public static BaseNode from(Node node) {
        String label = StreamSupport.stream(node.labels().spliterator(), false).findFirst().orElse("");
        switch (label) {
            case "":
                System.err.println("In BaseNode::from,No label found of node :" + node.id());
            case "Feature":
                return new Feature(node);
            case "FeatureGroup":
                return new FeatureGroup(node);
            case "Fault":
                return new Fault(node);
            case "Component":
                return new Component(node);
            case "Reason":
                return new Reason(node);
            default:
                return new BaseNode(node);
        }
    }


    public static BaseNode loadNodeById(Long id) {
        return BaseNode.from(graphData.getById(id));
    }

    public static Set<Long> getSubEdges(long fromId) {
        return graphData.getEdges().getOrDefault(fromId, Collections.emptySet());
    }

    public static boolean hasSubEdges(long fromId){
        Set<Long> subEdges = graphData.getEdges().get(fromId);
        return subEdges != null && subEdges.size() > 0;
    }

    public boolean hasSubEdges(){
        return hasSubEdges(this.getId());
    }

    public static ObjectCacheUtils<Long, BaseNode> nodeCache = new ObjectCacheUtils<>(BaseNode::loadNodeById);
    public static ObjectCacheUtils<Long, Set<Long>> edgeCache = new ObjectCacheUtils<>(BaseNode::getSubEdges);


    public static Stream<Long> getAllSubNodes(long rootId) {
        Set<Long> subE = edgeCache.get(rootId);
        if (subE == null || subE.size() == 0) {
            return Stream.empty();
        }
        return Stream.concat(subE.stream(), subE.stream().flatMap(BaseNode::getAllSubNodes));
    }
}
